﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace PlutoStudio
{
    /// <summary>
    /// 集合扩展方法类
    /// </summary>
    public static class CollectionsExtensions
    {
        #region 公共方法

        #region IEnumerable的扩展

        /// <summary>
        ///     将集合展开并分别转换成字符串，再以指定的分隔符衔接，拼成一个字符串返回
        /// </summary>
        /// <param name="collection"> 要处理的集合 </param>
        /// <param name="separator"> 分隔符 </param>
        /// <returns> 拼接后的字符串 </returns>
        public static string ExpandAndToString<T>(this IEnumerable<T> collection, string separator)
        {
            List<T> source = collection as List<T> ?? collection.ToList();
            if (source.IsEmpty())
            {
                return null;
            }
            return string.Join(separator, source);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static IEnumerable<T> Expand<T>(this IEnumerable<IEnumerable<T>> source)
        {
            var result = source.SelectMany(s => s.Select(i => i));
            return result;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static IQueryable<T> Expand<T>(this IQueryable<IQueryable<T>> source)
        {
            var result = source.SelectMany(s => s.Select(i => i));
            return result;
        }

        /// <summary>
        ///     将集合展开并分别将指定属性转换成字符串，再以指定的分隔符衔接，拼成一个字符串返回
        /// </summary>
        /// <param name="source"> 要处理的集合 </param>
        /// <param name="propertyExpression"></param>
        /// <param name="separator"> 分隔符 </param>
        /// <returns> 拼接后的字符串 </returns>
        public static string ExpandAndToString<TModel,TProperty>(this IEnumerable<TModel> source,Func<TModel,TProperty> propertyExpression, string separator)
        {
            if (source.IsNullOrEmpty())
            {
                return null;
            }
            return string.Join(separator, source.Select(propertyExpression));
        }

        /// <summary>
        ///     集合是否为空
        /// </summary>
        /// <param name="collection"> 要处理的集合 </param>
        /// <typeparam name="T"> 动态类型 </typeparam>
        /// <returns> 为空返回True，不为空返回False </returns>
        public static bool IsEmpty<T>(this IEnumerable<T> collection)
        {
            return !collection.Any();
        }

        /// <summary>
        /// 对枚举器的每个元素执行指定的操作
        /// </summary>
        /// <typeparam name="T">枚举器类型参数</typeparam>
        /// <param name="source">枚举器</param>
        /// <param name="action">要对枚举器的每个元素执行的委托</param>
        public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
        {
            if (source.IsNullOrEmpty() || action == null)
            {
                return;
            }
            foreach (var item in source)
            {
                action(item);
            }
        }

        /// <summary>
        /// 指示指定的枚举器是null还是没有任何元素
        /// </summary>
        /// <typeparam name="T">枚举器类型参数</typeparam>
        /// <param name="source">要测试的枚举器</param>
        /// <returns>true:枚举器是null或者没有任何元素 false:枚举器不为null并且包含至少一个元素</returns>
        public static bool IsNullOrEmpty<T>(this IEnumerable<T> source)
        {
            return source == null || !source.Any();
        }

        /// <summary>
        ///     根据指定条件返回集合中不重复的元素
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <typeparam name="TKey">动态筛选条件类型</typeparam>
        /// <param name="source">要操作的源</param>
        /// <param name="keySelector">重复数据筛选条件</param>
        /// <returns>不重复元素的集合</returns>
        public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector)
        {
            return source.GroupBy(keySelector).Select(group => group.First());
        }

        /// <summary>
        /// 如果condition指定的条件成立便执行predicate子句。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="predicate"></param>
        /// <param name="condition"></param>
        /// <returns></returns>
        public static IQueryable<T> WhereIf<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate, bool condition)
        {
            PublicHelper.CheckArgument(predicate, "predicate");
            return condition ? source.Where(predicate) : source;
        }
        /// <summary>
        /// 如果condition指定的条件成立便执行predicate子句。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="predicate"></param>
        /// <param name="condition"></param>
        /// <returns></returns>
        public static IEnumerable<T> WhereIf<T>(this IEnumerable<T> source, Func<T, bool> predicate, bool condition)
        {
            PublicHelper.CheckArgument(predicate, "predicate");
            return condition ? source.Where(predicate) : source;
        }


        #endregion

        #region IDictionary的扩展

        /// <summary>
        /// 将IDictonary的键值对转化成指定类型的对象。无法匹配的值将被忽略。
        /// </summary>
        /// <typeparam name="TObject">转化的目标对象类型</typeparam>
        /// <param name="dictionary">要转化的键值对</param>
        /// <returns>转化后的对象</returns>
        public static TObject ToObject<TObject>(this IDictionary<string, string> dictionary)
        {
            return (TObject)dictionary.ToObject(typeof(TObject));
        }

        /// <summary>
        /// 将IDictonary的键值对转化成指定类型的对象。无法匹配的值将被忽略。
        /// </summary>
        /// <param name="type">转化的目标对象类型</param>
        /// <param name="dictionary">要转化的键值对</param>
        /// <returns>转化后的对象</returns>
        public static object ToObject(this IDictionary<string, string> dictionary,Type type)
        {
            var result = Activator.CreateInstance(type);
            var properties = System.ComponentModel.TypeDescriptor.GetProperties(type);
            foreach (var item in dictionary)
            {
                var propertyName = item.Key;
                var property = properties.Find(propertyName, true);
                if (property == null)
                {
                    continue;
                }
                if (item.Value == null)
                {
                    continue;
                }
                var value = item.Value.Deserialize(property.PropertyType);
                property.SetValue(result, value);
            }
            return result;
        }


        /// <summary>
        /// 将IDictonary的键值对转化成指定类型的对象。无法匹配的值将被忽略。
        /// </summary>
        /// <typeparam name="TObject">转化的目标对象类型</typeparam>
        /// <param name="dictionary">要转化的键值对</param>
        /// <returns>转化后的对象</returns>
        public static TObject ToObject<TObject>(this IDictionary<string, object> dictionary)
        {
            var result = Activator.CreateInstance<TObject>();
            var type = System.ComponentModel.TypeDescriptor.GetProperties(typeof(TObject));
            foreach (var item in dictionary)
            {
                var propertyName = item.Key;
                var property = type.Find(propertyName, true);
                if (property == null)
                {
                    continue;
                }
                if (item.Value == null)
                {
                    continue;
                }
                var value = item.Value;
                property.SetValue(result, Convert.ChangeType(value, property.PropertyType));
            }
            return result;
        }

        /// <summary>
        /// 将IDictonary的键值对转化成指定类型的对象。无法匹配的值将被忽略。
        /// </summary>
        /// <typeparam name="TObject">转化的目标对象类型</typeparam>
        /// <param name="dictionary">要转化的键值对</param>
        /// <returns>转化后的对象</returns>
        public static object ToObject(this IDictionary<string, object> dictionary,Type type)
        {
            var result = Activator.CreateInstance(type);
            var properties = System.ComponentModel.TypeDescriptor.GetProperties(type);
            foreach (var item in dictionary)
            {
                var propertyName = item.Key;
                var property = properties.Find(propertyName, true);
                if (property == null)
                {
                    continue;
                }
                if (item.Value == null)
                {
                    continue;
                }
                var value = item.Value;
                property.SetValue(result, Convert.ChangeType( value,property.PropertyType));
            }
            return result;
        }


        #endregion
        #endregion
    }
}
